In the SpriteWorld files->Utils->Brian's Extensions folder are some free extensions to SpriteWorld that I've been working on in my free time. They allow sprites which are translucent, scaled or rotated. There are also files for doing several different kinds of lighting effects. Each file has fairly extensive comments and documentation. In general, each file has general documentation at the top and function specific documentation with the functions. They have been tested to work with 68K and PowerPC Macintoshes, and have been optimized in some cases with PowerPC assembly code. They work in 8-Bit or 16-Bit color (i.e. 256 colors or Thousands of Colors).
To use these, you simply include the files in your project file.
Also note that translucency, scaling and rotation cannot be combined. That is, you can't have a scaled translucent sprite. Although this wouldn't be to hard for the avid programmer. The blitters are fairly well commented.
In addition, none of these extensions were designed to work in a scrolling world. You can try to get them to work in a scrolling game, but there are no guarantees that they will work. Generally, any functions that don't work right when clipping is used won't work properly in a scrolling world. (Such as scaling and rotating.) Translucency and other lighting effects might work in a scrolling game, but haven't been tested.
The Extended Sprite Test shows how all of this functionality except the tinting works. The Lighting Demo shows how tinting works. Lit Breakout is a more detailed example of the LightingSquares functionality. All demos and code has been built and tested with Think C 6.0, CodeWarrior 10, and CodeWarrior Pro 1. The demos are not included in the main SpriteWorld package, but are included in the "SpriteWorld 2.1 Extra Demos" package.
Below I've copied sections from the top of each source file so you can see the basic documentation all in this one file:
These routines are 8-bit and 16-bit blitters that dynamically shrink or
stretch the sprite.
Use SWSetSpriteScaledSize to set the scaling size. When the sprite is drawn, it
will be stretched or shrunk to the new width and height you specify.
SWSetSpriteScaledSize takes a sprite pointer and a width and a height. e.g.:
SWSetSpriteScaledSize(mySpritePtr, 20, 50); // Scale it
Use a width or height <= 0 to turn off scaling. Note that just resetting
the sprite's draw procedure will not turn off scaling properly. Make sure to
call SWSetSpriteScaledSize with a negative width or height. e.g.:
SWSetSpriteScaledSize(mySpritePtr, -1, -1); // Reset it back to normal
The maximum scaled width is 800. Values larger than this are permissible as long as the Sprite is clipped so that the visible portion of the Sprite after being clipped is no larger than 800. For instance, if your SpriteWorld's window is 800 pixels wide and your Sprites are clipped to that window, then your may scale your Sprites larger than 800 pixels. Otherwise, you will get an assertion error if you try to scale them larger. You can get around this limitation if necessary; simply change the definition of kMaxScaledWidth at the beginning of BlitPixieScaled.c to any value, and your Sprites can then be scaled to that maximum size. There is no maximum scaled height; your height can be any size.
Also note that scaling is done while drawing to the screen. As a result, the
pixels in the frames don't match what shows up on the screen. This means that
functions which rely on the sprite's mask, such as SWPixelCollision, won't work as
expected. Thus using the pixel or mask collision routines with scaled sprites
won't work accurately.
SWSetSpriteScaledSize sets the scaling DrawProc for you automatically, so you don't
need to call SWSetSpriteDrawProc, unless you want to use a RectDrawProc instead of the
automatically assigned ScaledMaskDrawProc. It will also change the DrawProc back to one
of the standard BlitPixieMaskDrawProcs when you set the scaled size to -1 to turn off
scaling. Here is a list of the available DrawProcs:
// User DrawProcs:
BlitPixie8BitScaledRectDrawProc
BlitPixie16BitScaledRectDrawProc
// Sprite DrawProcs:
BP8BitScaledSpriteRectDrawProc
BP8BitScaledSpriteMaskDrawProc
BP16BitScaledSpriteRectDrawProc
BP16BitScaledSpriteMaskDrawProc
The User DrawProcs are available for you to use directly if you want to do some direct
scaling, such as scaling the contents of the work area as you copy it to the screen.
The Sprite DrawProcs are what you should use when assigning a Sprite a scaling
DrawProc. Do not get these two types of DrawProcs mixed up.
These are blitters for rendering rotated versions of a sprite. The API is simple. Use SWSetSpriteRotation to set the sprite's rotation. The amount of rotation must be between 0 to the constant kNumberOfRotationSteps. Calling SWSetSpriteRotation with 0 as the rotation will reset the sprite to use the standard blitting routines.
Example Use:
SWSetSpriteRotation(mySpritePtr, 3); // Rotate it
SWSetSpriteRotation(mySpritePtr, 0); // Reset it back to normal
Note that all rotation is clipped to frameRect of the sprite. In other words, the sprite is rotated within it's bounding rectangle. Thus if you have a long skinny sprite, part of it will be lost while rotating it. To fix this problem is to make sure that you store the sprite in the resource with enough white space around it that it can rotate fully and have nothing lost.
Also note that rotation is done while drawing to the screen. As a result, it can be fairly slow and the pixels in the frames don't match what shows up on the screen. This means that functions which rely on the sprite's mask, such as SWPixelCollision, won't work as expected. Thus using the pixel or mask collisions routines with rotated sprites won't work accurately.
Because blitting rotated sprites is slow and there are collision issues, there is a function called CreateRotatedFrameFromFrame that can be called to create a new frame object which contains a rotated version of a frame. In this way the frame is precomputed and blits quickly. Example Use:
for (currentRotation = 1; currentRotation < 8; currentRotation++)
CreateRotatedFrameFromFrame(gSpriteWorld,
myFrames[0],
&myFrames[currentRotation],
currentRotation);
Imagine an asteroids like game with a single ship image. This would fill up a frame array with rotated versions of the ship stored in frame zero.
One additional technical note. We need the sin and cos function to do this rotation. For speed we precompute the values of sin and cos corresponding to each amount of rotation. Since we often want to use 8, 16, 32, or 64 degrees of rotations, arrays of these are predefined in our code.
If we wish to use a different number for kNumberOfRotationSteps, then we need to uncomment the #define DYNAMICALLY_GENERATE_TABLE in the header file for this code. When DYNAMICALLY_GENERATE_TABLE is defined, we have to call InitializeRotationTables() to fill up the tables with values. This requires including MathLib. If this sounds confusing, just leave kNumberOfRotationSteps with the default value of 32 (or use one of 8, 16, 32, or 64).
** N.B. These functions are still relatively experimental. They work fine, but the public API needs work to achieve better effects and uses.
These routines are for an alternative style of getting lighting effects. It's main function is a Screen Blitting procedures that takes the offscreen work area and colors it as it copies it on screen. Thus the parts copied on screen can be made darker or lighter or a little reddish, etc.
To support a feeling of lights, we divide the screen into a map of tiny squares and associate a color and a level with each square. As we blit onto the screen, we use this map to color the image. The result is that the screen image looks nicely colorized. Imagine each square having a colored gel placed over top of it.
The smaller the squares in our map, the finer the grain of lighting but the greater the overhead. More space and speed are required. We also provide routines for setting and managing this lighting map, including setting light levels and combining lights (so blue lights and yellow lights will become greenish lights as they overlap).
These functions make use of the translucency table functions found in "SWTranslucentBlitters.c" and therefore that file needs to be present for this to work.
Use SWInitializeLightingSquares(spriteWorldPtr, 8); to initialize the use of squares. 8 in this case is the size of the squares in pixels.
to set a cell's value. Opacity is the translucency levels (as in the translucent blitters). relativeP is a boolean specifying whether to set the color and level directly or to blend it together with what is already in the cell. makeDirtyP forces sprite world to redraw that cell on the screen during the next draw loop.
Use:
SWLightUpArea (spriteWorldPtr, col, row,
color, level
bigLightP);
to light up an area around a cell. This is good for spotlights. bigLightP makes the light really big. Note that this just calls SWSetLightingSquare repeatedly.
SWDarkenLightingSquares (spriteWorldPtr); will blend black into every square in the grid to darken everything. This is a good example of how to achieve a lighting effect with these squares.
Use
SWExitLightingSquares ();
to free up the tables when quitting.
The Lit Breakout Demo is an example of how to use these functions in an app.
- Functions for tinting SpriteWorld graphics a particular color.
- Functions for using tinting to implement basic lighting techniques on
sprites and tiles.
This style of lighting works well with tiles with a single kind of light. This is because we precompute all of the sprites and tiles at the different light levels. This makes it fast, but not very flexible. See "SWLightingSquares.c" for another approach to lighting which is slightly slower, but is dynamic and handles an arbitrary number of colored lights.
Functionality documented above each function. See the Lighting Demo for a representative example of how to use everything.
This is all provided as is with no promises or guarantees. Don't count on me for support, service, or coherency. And don't blame anyone else for my mistakes. Thanks are always appreciated. Drop me a line if this stuff is useful.